/*
 * TorrentDownloaderFactory.java
 *
 * Created on 2. November 2003, 03:52
 * Copyright (C) Azureus Software, Inc, All Rights Reserved.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

package org.gudy.azureus2.core3.torrentdownloader;

import java.io.File;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;

import org.gudy.azureus2.core3.global.GlobalManager;
import org.gudy.azureus2.core3.torrentdownloader.impl.TorrentDownloaderImpl;
import org.gudy.azureus2.core3.torrentdownloader.impl.TorrentDownloaderManager;
import org.gudy.azureus2.core3.util.Debug;
import org.gudy.azureus2.core3.util.TorrentUtils;
import org.gudy.azureus2.core3.util.UrlUtils;

import com.aelitis.azureus.core.proxy.AEProxyFactory;
import com.aelitis.azureus.core.proxy.AEProxyFactory.PluginProxy;

/**
 * 
 * @author Tobias Minich
 */
public class TorrentDownloaderFactory {

    private static TorrentDownloaderImpl getClass(boolean logged) {
        try {
            return (TorrentDownloaderImpl) Class.forName(
                    "org.gudy.azureus2.core3.torrentdownloader.impl.TorrentDownloader" + (logged ? "Logged" : "") + "Impl").newInstance();
        } catch (Exception e) {
            Debug.printStackTrace(e);
            return null;
        }
    }

    /**
     * creates and initializes a TorrentDownloader object with the specified parameters. NOTE: this does not actually start the TorrentDownloader
     * object
     * 
     * @param callback
     *            object to notify about torrent download status
     * @param url
     *            url of torrent file to download
     * @param referrer
     *            url of referrer to set as HTTP_REFERER header when requesting torrent
     * @param fileordir
     *            path to a file or directory that the actual torrent file should be saved to. if a default save directory is not specified, this will
     *            be used instead. even if a default save directory is specified, if this parameter path refers to a file, the filename will be used
     *            when saving the torrent
     * @param whether
     *            or not logging is enabled for the torrent download. this is performed through the TorrentDownloaderLoggedImpl class which is only
     *            available in the uis project
     * @return
     */
    public static TorrentDownloader create(TorrentDownloaderCallBackInterface callback, String url, String referrer, String fileordir,
            boolean logged) {
        return (create(callback, url, referrer, null, fileordir, logged));
    }

    public static TorrentDownloader create(TorrentDownloaderCallBackInterface callback, String url, String referrer, String fileordir) {
        return create(callback, url, referrer, fileordir, false);
    }

    public static TorrentDownloader create(TorrentDownloaderCallBackInterface callback, String url, String referrer, Map request_properties,
            String fileordir) {
        return (create(callback, url, referrer, request_properties, fileordir, false));
    }

    private static TorrentDownloader create(TorrentDownloaderCallBackInterface callback, String url, String referrer, Map request_properties,
            String fileordir, boolean logged) {
        return (new TorrentDownloadRetrier(callback, url, referrer, request_properties, fileordir, logged));
    }

    public static TorrentDownloader create(TorrentDownloaderCallBackInterface callback, String url, boolean logged) {
        return create(callback, url, null, null, logged);
    }

    public static TorrentDownloader create(TorrentDownloaderCallBackInterface callback, String url) {
        return create(callback, url, null, null, false);
    }

    public static TorrentDownloader create(String url, String fileordir, boolean logged) {
        return create(null, url, null, fileordir, logged);
    }

    public static TorrentDownloader create(String url, String fileordir) {
        return create(null, url, null, fileordir, false);
    }

    public static TorrentDownloader create(String url, boolean logged) {
        return create(null, url, null, null, logged);
    }

    public static TorrentDownloader create(String url) {
        return create(null, url, null, null, false);
    }

    public static void initManager(GlobalManager gm, boolean logged, boolean autostart, String downloaddir) {
        TorrentDownloaderManager.getInstance().init(gm, logged, autostart, downloaddir);
    }

    public static TorrentDownloader downloadManaged(String url, String fileordir, boolean logged) {
        return TorrentDownloaderManager.getInstance().download(url, fileordir, logged);
    }

    public static TorrentDownloader downloadManaged(String url, String fileordir) {
        return TorrentDownloaderManager.getInstance().download(url, fileordir);
    }

    public static TorrentDownloader downloadManaged(String url, boolean logged) {
        return TorrentDownloaderManager.getInstance().download(url, logged);
    }

    public static TorrentDownloader downloadManaged(String url) {
        return TorrentDownloaderManager.getInstance().download(url);
    }

    private static class TorrentDownloadRetrier implements TorrentDownloader {
        final private String url;
        final private String referrer;
        final private Map request_properties;
        final private String fileordir;
        final private boolean logged;

        private volatile TorrentDownloaderImpl delegate;

        private volatile boolean cancelled;

        private volatile boolean sdp_set;
        private volatile String sdp_path;
        private volatile String sdp_file;

        private volatile boolean dfoc_set;
        private volatile boolean dfoc;
        private volatile boolean irc_set;
        private volatile boolean irc;

        private volatile String original_error;

        private TorrentDownloadRetrier(final TorrentDownloaderCallBackInterface _callback, String _url, String _referrer, Map _request_properties,
                String _fileordir, boolean _logged) {
            url = _url;
            referrer = _referrer;
            request_properties = _request_properties;
            fileordir = _fileordir;
            logged = _logged;

            TorrentDownloaderCallBackInterface callback = new TorrentDownloaderCallBackInterface() {
                private TorrentDownloaderCallBackInterface original_callback = _callback;

                private boolean no_retry = original_callback == null;

                private boolean init_reported = false;
                private boolean start_reported = false;
                private boolean finish_reported = false;

                private boolean proxy_tried = false;
                private PluginProxy plugin_proxy;

                public void TorrentDownloaderEvent(int state, TorrentDownloader _delegate) {
                    if (_delegate != delegate) {

                        return;
                    }

                    if (state == STATE_ERROR) {

                        if (original_error == null) {

                            original_error = delegate.getError();
                        }
                    }

                    if (plugin_proxy != null
                            && (state == STATE_FINISHED || state == STATE_DUPLICATE || state == STATE_CANCELLED || state == STATE_ERROR)) {

                        plugin_proxy.setOK(state != STATE_ERROR);

                        plugin_proxy = null;
                    }

                    synchronized (this) {

                        if (state == STATE_INIT) {

                            if (init_reported) {

                                return;
                            }

                            init_reported = true;
                        }

                        if (state == STATE_START) {

                            if (start_reported) {

                                return;
                            }

                            start_reported = true;
                        }

                        if (state == STATE_FINISHED) {

                            if (finish_reported) {

                                return;
                            }

                            finish_reported = true;
                        }
                    }

                    if (cancelled) {

                        no_retry = true;
                    }

                    if (no_retry) {

                        if (original_callback != null) {

                            original_callback.TorrentDownloaderEvent(state, TorrentDownloadRetrier.this);
                        }

                        return;
                    }

                    if (state == STATE_FINISHED || state == STATE_DUPLICATE || state == STATE_CANCELLED) {

                        if (state == STATE_FINISHED && proxy_tried) {

                            TorrentUtils.setObtainedFrom(delegate.getFile(), url);
                        }

                        if (original_callback != null) {

                            original_callback.TorrentDownloaderEvent(state, TorrentDownloadRetrier.this);
                        }

                        no_retry = true;

                        return;
                    }

                    if (state == STATE_ERROR) {

                        String lc_url = url.toLowerCase().trim();

                        if (!proxy_tried) {

                            proxy_tried = true;

                            if (lc_url.startsWith("http")) {

                                try {
                                    URL original_url = new URL(url);

                                    plugin_proxy = AEProxyFactory.getPluginProxy("loading plugin details", original_url);

                                    if (plugin_proxy != null) {

                                        delegate = TorrentDownloaderFactory.getClass(logged);

                                        if (sdp_set) {

                                            delegate.setDownloadPath(sdp_path, sdp_file);
                                        }

                                        if (dfoc_set) {

                                            delegate.setDeleteFileOnCancel(dfoc);
                                        }

                                        if (irc_set) {

                                            delegate.setIgnoreReponseCode(irc);
                                        }

                                        Map props = new HashMap();

                                        if (request_properties != null) {

                                            props.putAll(request_properties);
                                        }

                                        props.put("HOST", plugin_proxy.getURLHostRewrite()
                                                + (original_url.getPort() == -1 ? "" : (":" + original_url.getPort())));

                                        delegate.init(this, plugin_proxy.getURL().toExternalForm(), plugin_proxy.getProxy(), referrer, props,
                                                fileordir);

                                        delegate.start();

                                        return;
                                    }
                                } catch (Throwable e) {

                                }
                            }
                        }

                        String retry_url = null;

                        if (lc_url.startsWith("http")) {

                            retry_url = UrlUtils.parseTextForURL(url.substring(5), true);
                        }

                        if (retry_url != null) {

                            delegate = TorrentDownloaderFactory.getClass(logged);

                            if (sdp_set) {

                                delegate.setDownloadPath(sdp_path, sdp_file);
                            }

                            if (dfoc_set) {

                                delegate.setDeleteFileOnCancel(dfoc);
                            }

                            if (irc_set) {

                                delegate.setIgnoreReponseCode(irc);
                            }

                            delegate.init(this, retry_url, null, referrer, request_properties, fileordir);

                            no_retry = true;

                            delegate.start();

                            return;

                        } else {

                            no_retry = true;
                        }
                    }

                    if (original_callback != null) {

                        original_callback.TorrentDownloaderEvent(state, TorrentDownloadRetrier.this);
                    }
                }

            };

            delegate = TorrentDownloaderFactory.getClass(logged);

            delegate.init(callback, url, null, referrer, request_properties, fileordir);
        }

        public void start() {
            delegate.start();
        }

        public void cancel() {
            cancelled = true;

            delegate.cancel();
        }

        public void setDownloadPath(String path, String file) {
            sdp_set = true;
            sdp_path = path;
            sdp_file = file;

            delegate.setDownloadPath(path, file);
        }

        public int getDownloadState() {
            return (delegate.getDownloadState());
        }

        public File getFile() {
            return (delegate.getFile());
        }

        public int getPercentDone() {
            return (delegate.getPercentDone());
        }

        public int getTotalRead() {
            return (delegate.getTotalRead());
        }

        public String getError() {
            if (original_error != null) {

                return (original_error);
            }

            return (delegate.getError());
        }

        public String getStatus() {
            return (delegate.getStatus());
        }

        public String getURL() {
            return (delegate.getURL());
        }

        public int getLastReadCount() {
            return (delegate.getLastReadCount());
        }

        public byte[] getLastReadBytes() {
            return (delegate.getLastReadBytes());
        }

        public boolean getDeleteFileOnCancel() {
            return (delegate.getDeleteFileOnCancel());
        }

        public void setDeleteFileOnCancel(boolean deleteFileOnCancel) {
            dfoc_set = true;
            dfoc = deleteFileOnCancel;

            delegate.setDeleteFileOnCancel(deleteFileOnCancel);
        }

        public boolean isIgnoreReponseCode() {
            return (delegate.isIgnoreReponseCode());
        }

        public void setIgnoreReponseCode(boolean ignoreReponseCode) {
            irc_set = true;
            irc = ignoreReponseCode;

            delegate.setIgnoreReponseCode(ignoreReponseCode);
        }
    }
}
